/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.esgi.dns;

import java.nio.ByteBuffer;

/**
 * This class is used to create the Message Header section.
 *
 * http://www.ietf.org/rfc/rfc1035.txt
 * (§4.1.1. Header section format)
 * 
 *   The header contains the following fields:
 * 
 *                                   1  1  1  1  1  1
 *     0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
 *   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 *   |                      ID                       |
 *   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 *   |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
 *   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 *   |                    QDCOUNT                    |
 *   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 *   |                    ANCOUNT                    |
 *   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 *   |                    NSCOUNT                    |
 *   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 *   |                    ARCOUNT                    |
 *   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 * 
 * @author Alain Janin-Manificat <alain.janinm@hotmail.fr>
 * 
 * @see Message
 */
public class Header {

    /**
     * A 16 bit identifier assigned by the program that
     * generates any kind of query.  This identifier is copied
     * the corresponding reply and can be used by the requester
     * to match up replies to outstanding queries.
     */
    short id;
    
    /**
     * A one bit field that specifies whether this message is a
     * query (0), or a response (1).
     */
    boolean qr;
    
    /**
     * A four bit field that specifies kind of query in this
     * message.  This value is set by the originator of a query
     * and copied into the response.  The values are:
     * 
     * 0               a standard query (QUERY)
     * 
     * 1               an inverse query (IQUERY)
     * 
     * 2               a server status request (STATUS)
     * 
     * 3-15            reserved for future use
     */
    short opcode;
    
    /**
     * Authoritative Answer - this bit is valid in responses,
     * and specifies that the responding name server is an
     * authority for the domain name in question section.
     * 
     * Note that the contents of the answer section may have
     * multiple owner names because of aliases.  The AA bit
     * corresponds to the name which matches the query name, or
     * the first owner name in the answer section.
     */
    boolean aa;
    
    /**
     * TrunCation - specifies that this message was truncated
     * due to length greater than that permitted on the
     * transmission channel.
     */
    boolean tc;
    
    /**
     * Recursion Desired - this bit may be set in a query and
     * is copied into the response.  If RD is set, it directs
     * the name server to pursue the query recursively.
     * Recursive query support is optional.
     */
    boolean rd;
    
    /**
     * Recursion Available - this be is set or cleared in a
     * response, and denotes whether recursive query support is
     * available in the name server.
     */
    boolean ra;
    
    /**
     * Reserved for future use.  Must be zero in all queries
     * and responses.
     */
    short z;
    
    /**
     * Response code - this 4 bit field is set as part of
     * responses.  The values have the following
     * interpretation:
     * 
     * 0               No error condition
     * 
     * 1               Format error - The name server was
     * unable to interpret the query.
     *
     * 2               Server failure - The name server was
     * unable to process this query due to a
     * problem with the name server.
     * 
     * 3               Name Error - Meaningful only for
     * responses from an authoritative name
     * server, this code signifies that the
     * domain name referenced in the query does
     * not exist.
     * 
     * 4               Not Implemented - The name server does
     * not support the requested kind of query.
     * 
     * 5               Refused - The name server refuses to
     * perform the specified operation for
     * policy reasons.  For example, a name
     * server may not wish to provide the
     * information to the particular requester,
     * or a name server may not wish to perform
     * a particular operation (e.g., zone
     * transfer) for particular data.
     * 
     * 6-15            Reserved for future use.
     */
    short rcode;
    
    /**
     * An unsigned 16 bit integer specifying the number of
     * entries in the question section.
     */
    short qdcount;
    
    /**
     * An unsigned 16 bit integer specifying the number of
     * resource records in the answer section.
     */
    short ancount;
    
    /**
     * An unsigned 16 bit integer specifying the number of name
     * server resource records in the authority records section.
     */
    short nscount;
    
    /**
     * An unsigned 16 bit integer specifying the number of
     * resource records in the additional records section.
     */
    short arcount;
    
    /**
     * Size, in Byte, of a Header when stored in a Message.
     */
    public final static short HEADER_SIZE = 12;

    /**
     * Getter / Setter for Header fields.
     */
    public boolean isAa() {
        return aa;
    }

    public void setAa(boolean aa) {
        this.aa = aa;
    }

    public short getAncount() {
        return ancount;
    }

    public void setAncount(short ancount) {
        this.ancount = ancount;
    }

    public short getArcount() {
        return arcount;
    }

    public void setArcount(short arcount) {
        this.arcount = arcount;
    }

    public short getId() {
        return id;
    }

    public void setId(short id) {
        this.id = id;
    }

    public short getNscount() {
        return nscount;
    }

    public void setNscount(short nscount) {
        this.nscount = nscount;
    }

    public short getOpcode() {
        return opcode;
    }

    public void setOpcode(short opcode) {
        this.opcode = opcode;
    }

    public short getQdcount() {
        return qdcount;
    }

    public void setQdcount(short qdcount) {
        this.qdcount = qdcount;
    }

    public boolean isQr() {
        return qr;
    }

    public void setQr(boolean qr) {
        this.qr = qr;
    }

    public boolean isRa() {
        return ra;
    }

    public void setRa(boolean ra) {
        this.ra = ra;
    }

    public short getRcode() {
        return rcode;
    }

    public void setRcode(short rcode) {
        this.rcode = rcode;
    }

    public boolean isRd() {
        return rd;
    }

    public void setRd(boolean rd) {
        this.rd = rd;
    }

    public boolean isTc() {
        return tc;
    }

    public void setTc(boolean tc) {
        this.tc = tc;
    }

    public short getZ() {
        return z;
    }

    public void setZ(short z) {
        this.z = z;
    }

    public ByteBuffer encode() {
        return serialize(this);
    }

    /**
     * Serialize the Header informations in a ByteBuffer,
     * according to the rules defined in :
     * 
     * http://www.ietf.org/rfc/rfc1035.txt
     * (§4.1.1. Header section format)
     * 
     * @param m     Header 
     * @return      ByteBuffer - The Header informations stored in a ByteBuffer.
     *               The ByteBuffer is allocated with a capacity of 512 bytes.
     */
    public static ByteBuffer serialize(Header m) {
        short id = m.getId();

        short secLine = 0;
        secLine |= (m.isQr() ? 1 : 0) << 15;
        secLine |= (0xF & m.getOpcode()) << 11;
        secLine |= (m.isAa() ? 1 : 0) << 10;
        secLine |= (m.isTc() ? 1 : 0) << 9;
        secLine |= (m.isRd() ? 1 : 0) << 8;
        secLine |= (m.isRa() ? 1 : 0) << 7;
        secLine |= (0x7 & m.getZ()) << 4;
        secLine |= (0xF & m.getRcode());

        short qdCount = m.getQdcount();
        short anCount = m.getAncount();
        short nsCount = m.getNscount();
        short arCount = m.getArcount();

        /**
         * Allocate an initial ByteBuffer of 512 bytes.
         * Messages carried by UDP are restricted to 512 bytes (not counting the IP
         * or UDP headers).  Longer messages are truncated and the TC bit is set in
         * the header.
         */ 
        ByteBuffer bb = ByteBuffer.allocate(512);
        bb.putShort(id);
        bb.putShort(secLine);
        bb.putShort(qdCount);
        bb.putShort(anCount);
        bb.putShort(nsCount);
        bb.putShort(arCount);
        return bb;
    }

    /**
     * Unserialize a ByteBuffer containing Header informations
     * stored following the format defined in :
     * 
     * http://www.ietf.org/rfc/rfc1035.txt
     * (§4.1.1. Header section format)
     * 
     * Create an instance of Header from those informations.
     * 
     * @param input     ByteBuffer - Contains the header informations.
     * @return          Header - The Header object created from the ByteBuffer informations.
     */
    public static Header unserialize(ByteBuffer input) {
        Header h = new Header();
        h.setId(input.getShort(0));
        short secLine = input.getShort(2);
        boolean qr = (1 == ((0x8000 & secLine) >> 15));
        h.setQr(qr);
        short opCode = (short) ((0x7800 & secLine) >> 11);
        h.setOpcode(opCode);
        boolean aa = (1 == ((0x400 & secLine) >> 10));
        h.setAa(aa);
        boolean tc = (1 == ((0x200 & secLine) >> 9));
        h.setTc(tc);
        boolean rd = (1 == ((0x100 & secLine) >> 8));
        h.setRd(rd);
        boolean ra = (1 == ((0x80 & secLine) >> 7));
        h.setRa(ra);
        short z = (short) ((0x70 & secLine) >> 4);
        h.setZ(z);
        short rcode = (short) ((0xF & secLine));
        h.setRcode(rcode);
        h.setQdcount(input.getShort(4));
        h.setAncount(input.getShort(6));
        h.setNscount(input.getShort(8));
        h.setArcount(input.getShort(10));
        return h;
    }
}
